#include "SortedGraph.h"

#include <string>
#include <algorithm>
#include <map>

using namespace slaks::graphs;

struct NodeProperty
{
  long term_id;
	long term_frequency;
};

template <typename StringType = std::string>
struct EdgeProperty
{
  int section_type;
	StringType connecting_word;
	typedef StringType type;
};

typedef EdgeProperty<std::string> EdgePropertyString;

typedef sortable_graph<vecS,listS,directedS,NodeProperty,EdgePropertyString > graph_t;

typedef boost::property_map<graph_t::base_graph_t, long NodeProperty::* > :: type term_id_property_t;
typedef boost::property_map<graph_t::base_graph_t, long NodeProperty::* > :: type term_frequency_property_t;

typedef boost::property_map<graph_t::base_graph_t, int EdgePropertyString::* > :: type edge_section_type_property_t;
typedef boost::property_map<graph_t::base_graph_t, EdgePropertyString::type EdgePropertyString::* > :: type edge_connecting_word_property_t;

typedef boost::property_traits<edge_section_type_property_t>::value_type edge_section_value_type;
typedef boost::property_traits<edge_connecting_word_property_t>::value_type edge_connecting_word_value_type;





typedef std::map<long,graph_t::vertex_descriptor> vertex_map_t;


typedef boost::graph_traits<graph_t::base_graph_t>::vertex_descriptor vertex_t;
	typedef boost::graph_traits<graph_t::base_graph_t>::edge_descriptor edge_t;
	typedef boost::graph_traits<graph_t::base_graph_t>::vertices_size_type size_t;
	typedef boost::graph_traits<graph_t::base_graph_t>::out_edge_iterator out_edge_iter_t;
	typedef boost::graph_traits<graph_t::base_graph_t>::vertex_iterator vertex_iter_t;

template <typename Graph,
          typename TermIdMap, 
					typename TermFrequencyMap, 
					typename EdgeSectionTypeMap,
					typename EdgeConnectingWordMap
         >
void print_graph(const Graph &graph,
								 TermIdMap term_id_map,
								 TermFrequencyMap term_frequency_map,
								 EdgeSectionTypeMap edge_section_type_map,
								 EdgeConnectingWordMap edge_connecting_word_map
								 );

int main()
{
	

	
	//typedef boost::property_map<graph_t::base_graph_t, long NodeProperty::* > :: type term_id_property_t;
	
	graph_t graph;


	vertex_map_t vertex_map;
	typedef graph_t::vertex_descriptor vertex_t;
	typedef graph_t::edge_descriptor edge_t;

	vertex_t vertex;

	term_id_property_t term_id = get(&NodeProperty::term_id,graph);
	term_frequency_property_t term_frequency = get(&NodeProperty::term_frequency,graph);
  edge_section_type_property_t edge_section_type = get(&EdgePropertyString::section_type,graph);
	edge_connecting_word_property_t edge_connecting_word = get(&EdgePropertyString::connecting_word,graph);

  vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(10,vertex));
	graph.vertex_list.push_back(vertex);
	put(&NodeProperty::term_id,graph,vertex,10);
	put(&NodeProperty::term_frequency,graph,vertex,1);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(20,vertex));
	graph.vertex_list.push_back(vertex);
	put(&NodeProperty::term_id,graph,vertex,20);
	put(&NodeProperty::term_frequency,graph,vertex,4);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(30,vertex));
	graph.vertex_list.push_back(vertex);
	put(&NodeProperty::term_id,graph,vertex,30);
	put(&NodeProperty::term_frequency,graph,vertex,6);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(40,vertex));
	graph.vertex_list.push_back(vertex);
	put(&NodeProperty::term_id,graph,vertex,40);
	put(&NodeProperty::term_frequency,graph,vertex,13);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(50,vertex));
	graph.vertex_list.push_back(vertex);
	put(&NodeProperty::term_id,graph,vertex,50);
  put(&NodeProperty::term_frequency,graph,vertex,7);

	
	vertex_t *ver1,*ver2;
	edge_t edge;
	bool inserted;

	if ((ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map))) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver1,graph);
		put(&EdgePropertyString::section_type,graph,edge,999);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("slava,kisilevich"));
	}

	if ((ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map))) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver1,graph);
		put(&EdgePropertyString::section_type,graph,edge,11111);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("marina,litvak"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,888);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("puki,kuki"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(30,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,777);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string(""));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(30,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,666);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("business"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(30,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(40,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,555);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("pandemonium"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(40,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,444);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("something,nothing,on,the"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(50,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,333);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("kuki"));
	}


	print_graph(graph,term_id,term_frequency,edge_section_type,edge_connecting_word);


	term_id = boost::get(&NodeProperty::term_id,graph);
	graph_t::vertex_iterator v_iter,v_end;
	boost::tie(v_iter,v_end) = vertices(graph);
	NodeSort<graph_t,term_id_property_t> n(term_id);
	std::sort(v_iter,v_end,n);


	boost::tie(v_iter,v_end) = vertices(graph);
	for (;v_iter != v_end; ++v_iter)
	{
		std::cout << term_id[*v_iter] << std::endl;
	}
	
	term_id_property_t term_id2 = boost::get(&NodeProperty::term_id,graph);
	//graph_t *graph2 = set_intersection<graph_t>(v_iter,v_end,v_iter,v_end,term_id,term_id2,&NodeProperty::term_id);
	graph_t *graph2 = set_intersection2<graph_t>(graph,graph,&NodeProperty::term_id,&EdgeProperty<>::section_type);

	print_graph(*graph2,
		           boost::get(&NodeProperty::term_id,*graph2),
							 boost::get(&NodeProperty::term_frequency,*graph2),
							 boost::get(&EdgeProperty<>::section_type,*graph2),
							 boost::get(&EdgeProperty<>::connecting_word,*graph2));
}


template <typename Graph,
          typename TermIdMap, 
					typename TermFrequencyMap, 
					typename EdgeSectionTypeMap,
					typename EdgeConnectingWordMap
         >
void print_graph(const Graph &graph,
								 TermIdMap term_id_map,
								 TermFrequencyMap term_frequency_map,
								 EdgeSectionTypeMap edge_section_type_map,
								 EdgeConnectingWordMap edge_connecting_word_map
								 )
{
  

   vertex_iter_t vertex_i, vertex_end;
	 boost::tie(vertex_i,vertex_end) = boost::vertices(graph);

	 out_edge_iter_t out_edge_i, out_edge_end;
	 for (; vertex_i != vertex_end; ++vertex_i) //iterate through vertices
	 {
		 std::cout << "id : " << term_id_map[*vertex_i] << std::endl;
		 std::cout << "term_frequency_value : " << boost::get(term_frequency_map,*vertex_i) << std::endl;

		 for (boost::tie(out_edge_i,out_edge_end) = boost::out_edges(*vertex_i,graph); out_edge_i != out_edge_end; ++out_edge_i) //get all out edges of the vertex
		 {
			 vertex_t trgt = target(*out_edge_i,graph);

			 std::cout << "        connected to id : " << boost::get(term_id_map,trgt) << std::endl;
			 std::cout << "        term_frequency_value : " << boost::get(term_frequency_map,trgt) << std::endl;

			 edge_section_value_type edge_section_type = boost::get(edge_section_type_map,*out_edge_i);
			 edge_connecting_word_value_type edge_connecting_word_type = boost::get(edge_connecting_word_map,*out_edge_i);

			 std::cout << "        section type : " << edge_section_type << std::endl;
			 std::cout << "        connecting word  : " << edge_connecting_word_type << std::endl;


			 std::cout << "\n";
		 }

		 std::cout << "\n";
	 }
}